package fr.utbm.VI51.lemmings_world_editor;

import java.awt.Color;
import java.io.Serializable;


/**
 * @author Adrien
 * 
 * This class represent a map of a level with a height, width, and an array of elements
 *
 */
public class Map implements Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 2298853752307253000L;

	/**
	 * The height of the map in cases
	 */
	private int height;
	
	/**
	 * The width of the map in cases
	 */
	private int width;
	
	/**
	 * The map containing the elements
	 */
	private elements[][] map;
	
	/**
	 * <tt>true</tt> if the map have an entrance
	 */
	private boolean entrance;
	
	/**
	 * <tt>true</tt> if the map have an exit
	 */
	private boolean exit;
	
	/**
	 * Constructor of the map class which set the height and width
	 * and constructs borders with walls
	 * 
	 * @param height the height of the map
	 * @param width the width of the map
	 */
	Map(int height, int width){
		this.height = height;
		this.width = width;
		
		this.map = new elements[height][width];
		
		this.entrance = false;
		this.exit = false;
		
		//Construct borders with walls
		constructBordersAndClear();
	}
	
	/**
	 * Construct borders with walls and clear the others elements
	 */
	public void constructBordersAndClear(){
		for (int i = 0; i < this.width; i++) {
			this.map[0][i] = elements.ROCK;
			this.map[this.height-1][i] = elements.ROCK;
		}
		for (int i = 0; i < this.height; i++) {
			this.map[i][0] = elements.ROCK;
			this.map[i][this.width-1] = elements.ROCK;
		}
		//Set the other cases as air:
		for (int i = 1; i < this.height-1; i++) {
			for (int j = 1; j < this.width-1; j++) {
				this.map[i][j] = elements.AIR;
			}
		}
	}
	
	/**
	 * Set the entrance attribute of the map
	 * 
	 * @param entrance <tt>true</tt> if the map has an entrance
	 */
	public void setEntrance(boolean entrance){
		this.entrance = entrance;
	}
	
	/**
	 * Set the exit attribute of the map
	 * 
	 * @param exit <tt>true</tt> if the map has an entrance
	 */
	public void setExit(boolean exit){
		this.exit = exit;
	}
	
	/**
	 * Returns true if the map has an entrance
	 * 
	 * @return <tt>true</tt> if the map has an entrance
	 */
	public boolean hasEntrance(){
		return this.entrance;
	}
	
	/**
	 * Returns true if the map has an exit
	 * 
	 * @return <tt>true</tt> if the map has an exit
	 */
	public boolean hasExit(){
		return this.exit;
	}
	
	/**
	 * Returns the height of the map in cases
	 * 
	 * @return the height of the map
	 */
	public int getHeight() {
		return this.height;
	}

	/**
	 * Set the height of the map in cases
	 * 
	 * @param height the height of the map
	 */
	public void setHeight(int height) {
		this.height = height;
	}

	/**
	 * Returns the width of the map in cases
	 * 
	 * @return the width of the map
	 */
	public int getWidth() {
		return this.width;
	}

	/**
	 * Set the width of the map in cases
	 * 
	 * @param width the width of the map
	 */
	public void setWidth(int width) {
		this.width = width;
	}
	
	/**
	 * Set the element of a given case
	 * 
	 * @param caseX the x coordinate of the case
	 * @param caseY the y coordinate of the case
	 * @param element the element to put in the case (x,y)
	 */
	public void setElements(int caseX, int caseY, elements element, int arroundSize){
		//Test if the element is the entrance or the exit
		if((element == elements.ENTRANCE) || (element == elements.EXIT)){
			arroundSize = 1;
			if(element == elements.ENTRANCE && this.entrance == true)
				removeEntrance();
			if(element == elements.EXIT && this.exit == true)
				removeExit();
		}
		
		//Test if an exit or entrance will be added and set the corresponding boolean to true
		if(caseX != 0 && caseX != this.width-1 &&
				caseY != 0 && caseY != this.height-1){
			if(element == elements.ENTRANCE)
				this.entrance = true;
			if(element == elements.EXIT)
				this.exit = true;
		}
		//Set the elements in the corresponding cases
		for (int i = caseY - arroundSize + 1; i < caseY + arroundSize; i++) {
			for (int j = caseX - arroundSize + 1; j < caseX + arroundSize; j++) {
				if(j > 0 && j < this.width-1 &&
						i > 0 && i < this.height-1)
					this.map[i][j] = element;
			}
		}
	}

	/**
	 * Find the exit of the map and remove it
	 */
	private void removeExit() {
		for (int i = 0; i < this.height; i++) {
			for (int j = 0; j < this.width; j++) {
				if(this.map[i][j] == elements.EXIT){
					this.map[i][j] = elements.AIR;
					break;
				}
			}
		}
	}
	
	/**
	 * Returns the element stored at the (i,j) position
	 * 
	 * @param i the line
	 * @param j the column
	 * @return the element stored at the (i,j) position
	 */
	public elements getElementAt(int i, int j){
		return this.map[i][j];
	}

	/**
	 * Find the exit of the map and remove it
	 */
	private void removeEntrance() {
		for (int i = 0; i < this.height; i++) {
			for (int j = 0; j < this.width; j++) {
				if(this.map[i][j] == elements.ENTRANCE){
					this.map[i][j] = elements.AIR;
					break;
				}
			}
		}		
	}

	/**
	 * Returns the map
	 * 
	 * @return the map
	 */
	public elements[][] getMap() {
		return this.map;
	}
	
	/**
	 * Set the map
	 *  
	 * @param map the new map
	 */
	public void setMap(int height, int width){
		this.map = new elements[height][width];
		this.height = height;
		this.width = width;
		this.constructBordersAndClear();
	}
	
	/**
	 * @author Adrien
	 * The enumeration of the type of element the map can contain
	 */
	public enum elements {
		EARTH,
		ROCK,
		WATER,
		FIRE,
		AIR,
		ENTRANCE,
		EXIT;
	}
	
	/**
	 * Returns the color of a given element
	 * 
	 * @param element the element
	 * @return the element corresponding color
	 */
	public Color getElementColor(elements element){
		switch (element) {
		case EARTH:
			return Color.GRAY;
			
		case ROCK:
			return Color.BLACK;
			
		case WATER:
			return Color.BLUE;
			
		case FIRE:
			return Color.RED;
			
		case AIR:
			return Color.WHITE;
			
		case ENTRANCE:
			return Color.ORANGE;
			
		case EXIT:
			return Color.GREEN;

		default:
			return Color.WHITE;
		}
	}
	
	
}
